import { Injectable, BadRequestException, Inject } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { RedisService } from 'src/module/common/redis/redis.service';
import * as bcrypt from 'bcryptjs';
import { Response } from 'express';
import { GetNowDate, GenerateUUID, Uniq, sm2Decrypt } from 'src/common/utils/index';
import { ExportTable } from 'src/common/utils/export';

import { CacheEnum, DelFlagEnum, StatusEnum, DataScopeEnum } from 'src/common/enum/index';
import { LOGIN_TOKEN_EXPIRESIN, SYS_USER_TYPE } from 'src/common/constant/index';
import { ResultData } from 'src/common/utils/result';
import { CreateUserDto, UpdateUserDto, ListUserDto, ChangeStatusDto, ResetPwdDto, AllocatedListDto, UpdateProfileDto, UpdatePwdDto } from './dto/index';
import { RegisterDto, LoginDto } from '../../main/dto/index';
import { AuthUserCancelDto, AuthUserCancelAllDto, AuthUserSelectAllDto } from '../role/dto/index';

import { RoleService } from '../role/role.service';
import { DeptService } from '../dept/dept.service';

import { ConfigService } from '../config/config.service';
import { ConfigService as Config } from '@nestjs/config';
import { UserType } from './dto/user';
import { ClientInfoDto } from 'src/common/decorators/common.decorator';
import { Cacheable, CacheEvict } from 'src/common/decorators/redis.decorator';
import { Captcha } from 'src/common/decorators/captcha.decorator';
import { PrismaService } from '@/module/common/prisma/prisma.service';
import { Dept, Menu, Post, Role, User } from '@prisma/client';
import { pickEquals, pickLike } from '@/common/utils/prisma';
import { PrismaConst } from '@/common/constant/prisma.constant';
@Injectable()
export class UserService {
    constructor(
        private readonly roleService: RoleService,
        private readonly deptService: DeptService,
        private readonly jwtService: JwtService,
        private readonly redisService: RedisService,
        private readonly configService: ConfigService,
        private readonly prisma: PrismaService,
        private readonly config: Config,
    ) { }
    /**
     * 后台创建用户
     * @param createUserDto
     * @returns
     */
    async create(createUserDto: CreateUserDto) {
        const salt = bcrypt.genSaltSync(10);
        if (createUserDto.password) {
            createUserDto.password = await bcrypt.hashSync(createUserDto.password, salt);
        }

        // const res = await this.userRepo.save({ ...createUserDto, userType: SYS_USER_TYPE.CUSTOM });
        const user = await this.prisma.user.create({ data: { ...pickEquals(createUserDto, PrismaConst.user), userType: SYS_USER_TYPE.CUSTOM } });
        // 插入用户与岗位的关联
        const userPostRelations = createUserDto.postIds.map((postId) => {
            return {
                userId: user.userId,
                postId: postId,
            };
        });

        await this.prisma.userPost.createMany({
            data: userPostRelations,
        });

        // 插入用户与角色的关联
        const userRoleRelations = createUserDto.roleIds.map((roleId) => {
            return {
                userId: user.userId,
                roleId: roleId,
            };
        });

        await this.prisma.userRole.createMany({
            data: userRoleRelations
        });

        return ResultData.ok();
    }

    /**
     * 用户列表
     * @param query
     * @returns
     */
    async findAll(query: ListUserDto, user: UserType['user']) {
        let where: any = {
            delFlag: '0',
            ...pickEquals(query, ['status']),
            ...pickLike(query, ['phonenumber', 'userName'])
        }
        console.log(query, '用户列表');

        if (query?.beginTime && query?.endTime) {
            where.createTime = {
                gte: new Date(query.beginTime),
                lte: new Date(query.endTime),
            }
        }

        //数据权限过滤
        if (user) {
            const roles = user.roles;
            const deptIds = [];
            let dataScopeAll = false;
            let dataScopeSelf = false;
            for (let index = 0; index < roles.length; index++) {
                const role = roles[index];
                if (role.dataScope === DataScopeEnum.DATA_SCOPE_ALL) {
                    dataScopeAll = true;
                    break;
                } else if (role.dataScope === DataScopeEnum.DATA_SCOPE_CUSTOM) {
                    const roleWithDeptIds = await this.roleService.findRoleWithDeptIds(role.roleId);
                    deptIds.push(...roleWithDeptIds);
                } else if (role.dataScope === DataScopeEnum.DATA_SCOPE_DEPT || role.dataScope === DataScopeEnum.DATA_SCOPE_DEPT_AND_CHILD) {
                    const dataScopeWidthDeptIds = await this.deptService.findDeptIdsByDataScope(user.deptId, role.dataScope);
                    deptIds.push(...dataScopeWidthDeptIds);
                } else if (role.dataScope === DataScopeEnum.DATA_SCOPE_SELF) {
                    dataScopeSelf = true;
                }
            }
            if (!dataScopeAll) {
                if (deptIds.length > 0) {
                    where.deptId = {
                        in: deptIds
                    }
                } else if (dataScopeSelf) {
                    where.userId = user.userId
                }
            }
        }
        if (query.deptId) {
            const deptIds = await this.deptService.findDeptIdsByDataScope(+query.deptId, DataScopeEnum.DATA_SCOPE_DEPT_AND_CHILD);
            where.deptId = {
                in: deptIds
            }
        }

        let skip = void 0
        let take = void 0
        if (query.pageSize && query.pageNum) {
            skip = (query.pageNum - 1) * query.pageSize
            take = +query.pageSize
        }
        const [list, total] = await Promise.all([
            this.prisma.user.findMany({
                where,
                skip,
                take,
                include: {
                    dept: true, // 联查部门详情
                },
            }),
            this.prisma.user.count({ where }),
        ]);
        console.log(list, total, '查询用户');
        return ResultData.ok({
            list,
            total,
        });
    }

    /**
     * 用户角色+岗位信息
     * @returns
     */
    async findPostAndRoleAll() {
        const posts = await this.prisma.post.findMany({
            where: {
                delFlag: '0',
            },
        });
        const roles = await this.roleService.findRoles({
            where: {
                delFlag: '0',
            },
        });

        return ResultData.ok({
            posts,
            roles,
        });
    }

    @Cacheable(CacheEnum.SYS_USER_KEY, '{userId}')
    async findOne(userId: number) {
        const data = await this.prisma.user.findUnique({
            where: {
                delFlag: '0',
                userId,
            },
        });

        const dept = await this.prisma.dept.findUnique({
            where: {
                delFlag: '0',
                deptId: +data.deptId,
            },
        });
        data['dept'] = dept;

        const postList = await this.prisma.userPost.findMany({
            where: {
                userId
            },
        });
        const postIds = postList.map((item) => item.postId);
        const allPosts = await this.prisma.post.findMany({
            where: {
                delFlag: '0',
            },
        });

        const roleIds = await this.getRoleIds([userId]);
        const allRoles = await this.roleService.findRoles({
            where: {
                delFlag: '0',
            },
        });

        data['roles'] = allRoles.filter((item) => roleIds.includes(item.roleId));

        return ResultData.ok({
            data,
            postIds,
            posts: allPosts,
            roles: allRoles,
            roleIds,
        });
    }

    /**
     * 更新用户
     * @param updateUserDto
     * @returns
     */
    @CacheEvict(CacheEnum.SYS_USER_KEY, '{updateUserDto.userId}')
    async update(updateUserDto: UpdateUserDto, userId: number) {
        //不能修改超级管理员
        if (updateUserDto.userId === 1) throw new BadRequestException('非法操作！');

        //过滤掉设置超级管理员角色
        updateUserDto.roleIds = updateUserDto.roleIds.filter((v) => v !== 1);

        //当前用户不能修改自己的状态
        if (updateUserDto.userId === userId) {
            delete updateUserDto.status;
        }

        if (updateUserDto?.postIds?.length > 0) {
            //用户已有岗位,先删除所有关联岗位
            const hasPostId = await this.prisma.userPost.findFirst({
                where: { userId: updateUserDto.userId },
                select: { postId: true },
            })

            if (hasPostId) {
                await this.prisma.userPost.deleteMany({
                    where: {
                        userId: updateUserDto.userId,
                    }
                })
            }
            const postValues = updateUserDto.postIds.map((id) => {
                return {
                    userId: updateUserDto.userId,
                    postId: id,
                };
            });
            await this.prisma.userPost.createMany({
                data: postValues,
            })
        }

        if (updateUserDto?.roleIds?.length > 0) {
            //用户已有角色,先删除所有关联角色
            const hasRoletId = await this.prisma.userRole.findFirst({
                where: { userId: updateUserDto.userId },
                select: { roleId: true },
            })
            if (hasRoletId) {
                await this.prisma.userRole.deleteMany({
                    where: {
                        userId: updateUserDto.userId
                    }
                });
            }
            const roleValues = updateUserDto.roleIds.map((id) => {
                return {
                    userId: updateUserDto.userId,
                    roleId: id,
                };
            });
            await this.prisma.userRole.createMany({
                data: roleValues,
            })
        }

        delete updateUserDto.password;
        delete (updateUserDto as any).dept;
        delete (updateUserDto as any).roles;
        delete (updateUserDto as any).roleIds;
        delete (updateUserDto as any).postIds;

        //更新用户信息
        const data = await this.prisma.user.update({
            where: {
                userId: updateUserDto.userId,
            },
            data: pickEquals(updateUserDto, PrismaConst.user),
        })
        return ResultData.ok(data);
    }

    @CacheEvict(CacheEnum.SYS_USER_KEY, '{userId}')
    clearCacheByUserId(userId: number) {
        return userId;
    }

    /**
     * 登陆
     */
    @Captcha('user')
    async login(user: LoginDto, clientInfo: ClientInfoDto) {
        console.log('进入user-service', user);
        const data = await this.prisma.user.findFirst({
            where: {
                userName: user.username,
            },
            select: {
                userId: true,
                password: true,
            }
        })
        const privateKey = this.config.get('encryption.privateKey');

        user.password = sm2Decrypt(privateKey, user.password)

        this.clearCacheByUserId(data.userId);

        if (!(data && bcrypt.compareSync(user.password, data.password))) {
            return ResultData.fail(500, `帐号或密码错误`);
        }

        const userData = await this.getUserinfo(data.userId);

        if (userData.delFlag === DelFlagEnum.DELETE) {
            return ResultData.fail(500, `您已被禁用，如需正常使用请联系管理员`);
        }
        if (userData.status === StatusEnum.STOP) {
            return ResultData.fail(500, `您已被停用，如需正常使用请联系管理员`);
        }

        /**
         * 更新用户登录信息
         */
        const loginDate = new Date();
        await this.prisma.user.update({
            where: {
                userId: data.userId,
            },
            data: {
                loginDate: loginDate,
                loginIp: clientInfo.ipaddr,
            },
        });
        const uuid = GenerateUUID();
        const token = this.createToken({ uuid, userId: userData.userId });
        const permissions = await this.getUserPermissions(userData.userId);
        const deptData = await this.prisma.dept.findUnique({
            where: {
                deptId: userData.deptId,
            },
            select: {
                deptName: true
            }
        });

        /**
         * 设置公司名称
         */
        userData['deptName'] = deptData.deptName || '';
        const roles = userData.roles.map((item) => item.roleKey);

        const userInfo = {
            browser: clientInfo.browser,
            ipaddr: clientInfo.ipaddr,
            loginLocation: clientInfo.loginLocation,
            loginTime: loginDate,
            os: clientInfo.os,
            permissions: permissions,
            roles: roles,
            token: uuid,
            user: userData,
            userId: userData.userId,
            username: userData.userName,
            deptId: userData.deptId,
        };

        await this.updateRedisToken(uuid, userInfo);

        return ResultData.ok(
            {
                token,
            },
            '登录成功',
        );
    }

    /**
     * 更新redis中用户权限和角色信息
     */
    async updateRedisUserRolesAndPermissions(uuid: string, userId: number) {
        const userData = await this.getUserinfo(userId);

        const permissions = await this.getUserPermissions(userId);
        const roles = userData.roles.map((item) => item.roleKey);

        await this.updateRedisToken(uuid, {
            permissions: permissions,
            roles: roles,
        });
    }

    /**
     * 更新redis中的元数据
     * @param token
     * @param metaData
     */
    async updateRedisToken(token: string, metaData: Partial<UserType>) {
        const oldMetaData = await this.redisService.get(`${CacheEnum.LOGIN_TOKEN_KEY}${token}`);

        let newMetaData = metaData;
        if (oldMetaData) {
            newMetaData = Object.assign(oldMetaData, metaData);
        }

        await this.redisService.set(`${CacheEnum.LOGIN_TOKEN_KEY}${token}`, newMetaData, LOGIN_TOKEN_EXPIRESIN);
    }

    /**
     * 获取角色Id列表
     * @param userId
     * @returns
     */
    async getRoleIds(userIds: Array<number>) {
        const roleList = await this.prisma.userRole.findMany({
            where: {
                userId: {
                    in: userIds, // 使用 Prisma 的 `in` 操作符
                },
            },
            select: {
                roleId: true, // 使用 Prisma 的 `select` 语法
            },
        });

        const roleIds = roleList.map((item) => item.roleId);
        return Uniq(roleIds);
    }

    /**
     * 获取权限列表
     * @param userId
     * @returns
     */
    async getUserPermissions(userId: number) {
        // 超级管理员 - 根据角色赋予 权限
        // if (userId === 1) {
        //   return ['*:*:*'];
        // }
        const roleIds = await this.getRoleIds([userId]);
        const list = await this.roleService.getPermissionsByRoleIds(roleIds);
        const permissions = Uniq(list.map((item: Menu) => item.perms)).filter((item) => {
            return item;
        });
        return permissions;
    }

    /**
     * 获取用户信息
     */
    async getUserinfo(userId: number): Promise<{ dept: Dept; roles: Array<Role>; posts: Array<Post> } & User> {

        const roleIds = await this.getRoleIds([userId]);

        const roles = await this.roleService.findRoles({
            where: {
                delFlag: '0',
                roleId: {
                    in: roleIds
                }
            },
        });
        const postIds = (
            await this.prisma.userPost.findMany({
                where: {
                    userId: userId,
                },
                select: {
                    postId: true
                },
            })
        ).map((item) => item.postId);

        const posts = await this.prisma.post.findMany({
            where: {
                delFlag: '0',
                postId: {
                    in: postIds
                },
            },
        });

        const data = await this.prisma.user.findUnique({
            where: {
                userId: userId,
                delFlag: DelFlagEnum.NORMAL,
            },
            include: {
                dept: true, // 联查部门详情
            },
        });

        const result = {
            ...data,
            roles,
            posts,
            dept: (data as any).dept,
        };

        return result;
    }

    /**
     * 注册
     */
    async register(user: RegisterDto) {
        const loginDate = GetNowDate();
        const checkUserNameUnique = await this.prisma.user.findUnique({
            where: {
                userName: user.username,
            },
            select: {
                userName: true
            },
        });
        if (checkUserNameUnique) {
            return ResultData.fail(500, `保存用户'${user.username}'失败，注册账号已存在`);
        }
        user['userName'] = user.username;
        user['nickName'] = user.username;
        await this.prisma.user.create({ data: pickEquals({ ...user, loginDate }, PrismaConst.user) });
        return ResultData.ok();
    }

    /**
     * 从数据声明生成令牌
     *
     * @param payload 数据声明
     * @return 令牌
     */
    createToken(payload: { uuid: string; userId: number }): string {
        const accessToken = this.jwtService.sign(payload);
        return accessToken;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    parseToken(token: string) {
        try {
            if (!token) return null;
            const payload = this.jwtService.verify(token.replace('Bearer ', ''));
            return payload;
        } catch (error) {
            return null;
        }
    }

    /**
     * 重置密码
     * @param body
     * @returns
     */
    async resetPwd(body: ResetPwdDto) {
        if (body.userId === 1) {
            return ResultData.fail(500, '系统用户不能重置密码');
        }
        if (body.password) {
            body.password = await bcrypt.hashSync(body.password, bcrypt.genSaltSync(10));
        }
        await this.prisma.user.update({
            where: {
                userId: body.userId,
            },
            data: {
                password: body.password,
            }
        });
        return ResultData.ok();
    }

    /**
     * 批量删除用户
     * @param ids
     * @returns
     */
    async remove(ids: number[]) {
        // 忽略系统角色的删除
        const data = await this.prisma.user.updateMany({
            where: {
                userId: { in: ids }, // userId 在 ids 数组中
                userType: { not: SYS_USER_TYPE.SYS }, // 忽略系统角色
            },
            data: {
                delFlag: '1', // 更新 delFlag 为 '1'
            },
        });

        return ResultData.ok(data);
    }

    /**
     * 角色详情
     * @param id
     * @returns
     */
    async authRole(userId: number) {
        const allRoles = await this.roleService.findRoles({
            where: {
                delFlag: '0',
            },
        });

        const user = await this.prisma.user.findUnique({
            where: {
                delFlag: '0',
                userId,
            },
        });

        const dept = await this.prisma.dept.findUnique({
            where: {
                delFlag: '0',
                deptId: +user.deptId,
            },
        });
        user['dept'] = dept;

        const roleIds = await this.getRoleIds([userId]);
        //TODO flag用来给前端表格标记选中状态，后续优化
        user['roles'] = allRoles.filter((item) => {
            if (roleIds.includes(item.roleId)) {
                item['flag'] = true;
                return true;
            } else {
                return true;
            }
        });

        return ResultData.ok({
            roles: allRoles,
            user,
        });
    }

    /**
     * 更新用户角色信息
     * @param query
     * @returns
     */
    async updateAuthRole(query) {
        const roleIds = query.roleIds.split(',');
        if (roleIds?.length > 0) {
            //用户已有角色,先删除所有关联角色
            const hasRoletId = await this.prisma.userRole.findFirst({
                where: {
                    userId: +query.userId,
                },
                select: {
                    roleId: true
                },
            });
            if (hasRoletId) {
                await this.prisma.userRole.deleteMany({
                    where: {
                        userId: +query.userId,
                    }
                });
            }
            const roleValues = roleIds.map((id) => {
                return {
                    userId: +query.userId,
                    roleId: +id,
                };
            });
            await this.prisma.userRole.createMany({ data: roleValues })
        }
        return ResultData.ok();
    }

    /**
     * 修改用户状态
     * @param changeStatusDto
     * @returns
     */
    async changeStatus(changeStatusDto: ChangeStatusDto) {
        const userData = await this.prisma.user.findUnique({
            where: {
                userId: changeStatusDto.userId,
            },
            select: {
                userType: true
            },
        });
        if (userData.userType === SYS_USER_TYPE.SYS) {
            return ResultData.fail(500, '系统角色不可停用');
        }
        const res = await this.prisma.user.update({
            where: {
                userId: changeStatusDto.userId,
            },
            data: {
                status: changeStatusDto.status,
            },
        })
        return ResultData.ok(res);
    }

    /**
     * 部门树
     * @returns
     */
    async deptTree() {
        const tree = await this.deptService.deptTree();
        return ResultData.ok(tree);
    }

    /**
     * 获取角色已分配用户
     * @param query
     * @returns
     */
    async allocatedList(query: AllocatedListDto) {
        const roleWidthRoleList = await this.prisma.userRole.findMany({
            where: {
                roleId: +query.roleId,
            },
            select: {
                userId: true
            },
        });
        if (roleWidthRoleList.length === 0) {
            return ResultData.ok({
                list: [],
                total: 0,
            });
        }
        const userIds = roleWidthRoleList.map((item) => item.userId);
        let where: any = {
            delFlag: '0',
            status: '0',
            userId: {
                in: userIds
            },
            ...pickLike(query, ['userName', 'phonenumber'])
        };
        let skip = void 0;
        let take = void 0;
        if (query.pageSize && query.pageNum) {
            skip = query.pageSize * (query.pageNum - 1);
            take = +query.pageSize;
        }
        const [list, total] = await Promise.all([
            this.prisma.user.findMany({
                where,
                skip,
                take,
                include: { dept: true }
            }),
            this.prisma.user.count({
                where
            })
        ]);
        return ResultData.ok({
            list,
            total,
        });
    }

    /**
     * 获取角色未分配用户
     * @param query
     * @returns
     */
    async unallocatedList(query: AllocatedListDto) {
        const roleWidthRoleList = await this.prisma.userRole.findMany({
            where: {
                roleId: +query.roleId,
            },
            select: {
                userId: true
            },
        });

        const userIds = roleWidthRoleList.map((item) => item.userId);

        let where: any = {
            delFlag: '0',
            status: '0',  // 状态为启用
            userId: { notIn: userIds }, // 排除已分配角色的用户
            ...pickLike(query, ['userName', 'phonenumber'])
        };
        let skip = void 0;
        let take = void 0;
        if (query.pageSize && query.pageNum) {
            skip = query.pageSize * (query.pageNum - 1);
            take = +query.pageSize;
        }
        const [list, total] = await Promise.all([
            this.prisma.user.findMany({
                where,
                skip,
                take,
                include: { dept: true }
            }),
            this.prisma.user.count({
                where
            })
        ]);
        return ResultData.ok({
            list,
            total,
        });
    }

    /**
     * 用户解绑角色
     * @param data
     * @returns
     */
    async authUserCancel(data: AuthUserCancelDto) {
        await this.prisma.userRole.deleteMany({
            where: {
                userId: data.userId,
                roleId: data.roleId,
            }
        });
        return ResultData.ok();
    }

    /**
     * 用户批量解绑角色
     * @param data
     * @returns
     */
    async authUserCancelAll(data: AuthUserCancelAllDto) {
        const userIds = data.userIds.split(',').map((id) => +id);
        await this.prisma.userRole.deleteMany({
            where: {
                userId: {
                    in: userIds,
                },
                roleId: data.roleId,
            },
        });
        return ResultData.ok();
    }

    /**
     * 用户批量绑定角色
     * @param data
     * @returns
     */
    async authUserSelectAll(data: AuthUserSelectAllDto) {
        const userIds = data.userIds.split(',');
        const dataToInsert = userIds.map(userId => ({
            userId: +userId,
            roleId: +data.roleId
        }))
        await this.prisma.userRole.createMany({
            data: dataToInsert
        })
        return ResultData.ok();
    }

    /**
     * 个人中心-用户信息
     * @param user
     * @returns
     */
    async profile(user) {
        return ResultData.ok(user);
    }

    /**
     * 个人中心-用户信息
     * @param user
     * @returns
     */
    async updateProfile(user: UserType, updateProfileDto: UpdateProfileDto) {
        await this.prisma.user.update({
            where: { userId: user.user.userId },
            data: pickEquals(updateProfileDto, PrismaConst.user),
        });
        const userData = await this.redisService.get(`${CacheEnum.LOGIN_TOKEN_KEY}${user.token}`);
        userData.user = Object.assign(userData.user, updateProfileDto);
        await this.redisService.set(`${CacheEnum.LOGIN_TOKEN_KEY}${user.token}`, userData);
        return ResultData.ok();
    }

    /**
     * 个人中心-修改密码
     * @param user
     * @param updatePwdDto
     * @returns
     */
    async updatePwd(user: UserType, updatePwdDto: UpdatePwdDto) {
        if (updatePwdDto.oldPassword === updatePwdDto.newPassword) {
            return ResultData.fail(500, '新密码不能与旧密码相同');
        }
        if (bcrypt.compareSync(user.user.password, updatePwdDto.oldPassword)) {
            return ResultData.fail(500, '修改密码失败，旧密码错误');
        }

        const password = await bcrypt.hashSync(updatePwdDto.newPassword, bcrypt.genSaltSync(10));
        await this.prisma.user.update({
            where: { userId: user.user.userId },
            data: { password },
        });
        return ResultData.ok();
    }

    /**
     * 导出用户信息数据为xlsx
     * @param res
     */
    async export(res: Response, body: ListUserDto, user: UserType['user']) {
        delete body.pageNum;
        delete body.pageSize;
        const list = await this.findAll(body, user);
        const options = {
            sheetName: '用户数据',
            data: list.data.list,
            header: [
                { title: '用户序号', dataIndex: 'userId' },
                { title: '登录名称', dataIndex: 'userName' },
                { title: '用户昵称', dataIndex: 'nickName' },
                { title: '用户邮箱', dataIndex: 'email' },
                { title: '手机号码', dataIndex: 'phonenumber' },
                { title: '用户性别', dataIndex: 'sex' },
                { title: '账号状态', dataIndex: 'status' },
                { title: '最后登录IP', dataIndex: 'loginIp' },
                { title: '最后登录时间', dataIndex: 'loginDate', width: 20 },
                { title: '部门', dataIndex: 'dept.deptName' },
                { title: '部门负责人', dataIndex: 'dept.leader' },
            ],
        };
        ExportTable(options, res);
    }
}
